home *** CD-ROM | disk | FTP | other *** search
/ Rock County Sheriff's Off…nternet Safety Initative / Rock County Sher.iso / pc / js / jquery.jtweetsanywhere.js < prev    next >
Encoding:
JavaScript  |  2012-09-13  |  68.2 KB  |  2,242 lines

  1. /**
  2.  * jTweetsAnywhere V1.3.1
  3.  * http://thomasbillenstein.com/jTweetsAnywhere/
  4.  *
  5.  * Copyright 2011, Thomas Billenstein
  6.  * Licensed under the MIT license.
  7.  * http://thomasbillenstein.com/jTweetsAnywhere/license.txt
  8.  */
  9.  
  10.  
  11. /**
  12.  * The code below is used as supplied by Twitter (https://dev.twitter.com/docs/intents)
  13.  *
  14.  * Twitter says:
  15.  
  16.  * "Some sites may prefer to embed the unobtrusive Web Intents pop-up Javascript inline
  17.  * or without a dependency to platform.twitter.com. The snippet below will offer the
  18.  * equivalent functionality without the external dependency."
  19.  */
  20. (function()
  21. {
  22.   if (window.__twitterIntentHandler)
  23.       return;
  24.  
  25.   var intentRegex = /twitter\.com(\:\d{2,4})?\/intent\/(\w+)/,
  26.       windowOptions = 'scrollbars=yes,resizable=yes,toolbar=no,location=yes',
  27.       width = 550,
  28.       height = 420,
  29.       winHeight = screen.height,
  30.       winWidth = screen.width;
  31.  
  32.  
  33.   function handleIntent(e)
  34.   {
  35.     e = e || window.event;
  36.  
  37.     var target = e.target || e.srcElement,
  38.         m, left, top;
  39.  
  40.     while (target && target.nodeName.toLowerCase() !== 'a')
  41.     {
  42.       target = target.parentNode;
  43.     }
  44.  
  45.     if (target && target.nodeName.toLowerCase() === 'a' && target.href)
  46.     {
  47.       m = target.href.match(intentRegex);
  48.       if (m)
  49.       {
  50.         left = Math.round((winWidth / 2) - (width / 2));
  51.         top = 0;
  52.  
  53.         if (winHeight > height)
  54.         {
  55.           top = Math.round((winHeight / 2) - (height / 2));
  56.         }
  57.  
  58.         window.open(target.href, 'intent', windowOptions + ',width=' + width + ',height=' + height + ',left=' + left + ',top=' + top);
  59.         e.returnValue = false;
  60.         e.preventDefault && e.preventDefault();
  61.       }
  62.     }
  63.   }
  64.  
  65.   if (document.addEventListener)
  66.   {
  67.     document.addEventListener('click', handleIntent, false);
  68.   }
  69.   else if (document.attachEvent)
  70.   {
  71.     document.attachEvent('onclick', handleIntent);
  72.   }
  73.  
  74.   window.__twitterIntentHandler = true;
  75. }());
  76.  
  77.  
  78. /**
  79.  * JTA_I18N is based on SimpleI18N V0.1.0
  80.  *
  81.  * SimpleI18N.js is a tiny library for simple i18n support in Javascript.
  82.  * Currently only translation is supported.
  83.  */
  84. (function()
  85. {
  86.     if (window.__JTA_I18N)
  87.     {
  88.         return;
  89.     }
  90.  
  91.     JTA_I18N = function()
  92.     {
  93.         var _resources = {};
  94.  
  95.         function ResourceBundle(locale, resources)
  96.         {
  97.             this.getLocale = function()
  98.             {
  99.                 return locale;
  100.             };
  101.  
  102.             this.get = function(key, params)
  103.             {
  104.                 return xlate(key, 1, params);
  105.             };
  106.  
  107.             this._ = this.get;
  108.  
  109.             this.nget = function(singular, plural, count, params)
  110.             {
  111.                 return count === 1 ? xlate(singular, 1, params) : xlate(plural, count, params);
  112.             };
  113.  
  114.             this.__ = this.nget;
  115.  
  116.             function xlate(key, count, params)
  117.             {
  118.                 var resource = getValue(key);
  119.  
  120.                 if (count !== 1 && typeof resource === "object")
  121.                 {
  122.                     resource = evalMulti(key, resource, count);
  123.                 }
  124.  
  125.                 if (resource && params)
  126.                 {
  127.                     for (p in params)
  128.                     {
  129.                         resource = resource.replace(p, getValue(params[p]));
  130.                     }
  131.                 }
  132.  
  133.                 return resource;
  134.             };
  135.  
  136.             function getValue(resource)
  137.             {
  138.                 return resources ? (resources[resource] || resource) : resource;
  139.             };
  140.  
  141.             function evalMulti(key, resource, count)
  142.             {
  143.                 for (pat in resource)
  144.                 {
  145.                     var re = /(\d+)\s*-\s*(\d+)/,
  146.                     match = re.exec(pat);
  147.  
  148.                     if (match)
  149.                     {
  150.                         var from = match[1];
  151.                         var to = match[2];
  152.                         if (count >= from && count <= to)
  153.                         {
  154.                             return resource[pat];
  155.                         }
  156.                     }
  157.  
  158.                     re = /([<>]=?)\s*(\d+)/;
  159.                     match = re.exec(pat);
  160.  
  161.                     if (match)
  162.                     {
  163.                         var op = match[1];
  164.                         var num = match[2];
  165.                         if (op === '>' && count > num)
  166.                         {
  167.                             return resource[pat];
  168.                         }
  169.                         else if (op === '>=' && count >= num)
  170.                         {
  171.                             return resource[pat];
  172.                         }
  173.                         else if (op === '<' && count < num)
  174.                         {
  175.                             return resource[pat];
  176.                         }
  177.                         else if (op === '<=' && count <= num)
  178.                         {
  179.                             return resource[pat];
  180.                         }
  181.                     }
  182.  
  183.                     re = /\s*,\s*/;
  184.                     match = pat.split(re);
  185.  
  186.                     if (match)
  187.                     {
  188.                         for (var i = 0; i < match.length; i++)
  189.                         {
  190.                             if (count === ~~match[i])
  191.                             {
  192.                                 return resource[pat];
  193.                             }
  194.                         }
  195.                     }
  196.                 }
  197.  
  198.                 return key;
  199.             }
  200.         };
  201.  
  202.         return {
  203.  
  204.             addResourceBundle: function(project, locale, resources)
  205.             {
  206.                 if (!_resources[project])
  207.                 {
  208.                     _resources[project] = {};
  209.                 }
  210.  
  211.                 _resources[project][locale] = resources;
  212.             },
  213.  
  214.             getResourceBundle: function(project, locale)
  215.             {
  216.                 return new ResourceBundle(locale, _resources[project] ? _resources[project][locale] : null);
  217.             }
  218.         };
  219.     }();
  220.  
  221.     window.__JTA_I18N = true;
  222. }());
  223.  
  224. JTA_I18N.addResourceBundle('jTweetsAnywhere', 'en',
  225. {
  226.     '$$monthNames': [ "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" ]
  227. });
  228.  
  229. (function($)
  230. {
  231.     $.fn.jTweetsAnywhere = function(config)
  232.     {
  233.         // setup the default options
  234.         var options = $.extend(
  235.         {
  236.             /**
  237.              * The user's name who's tweet feed or list feed is displayed. This
  238.              * param is also used when a Twitter "Follow Button" is displayed. Usually
  239.              * this param is a string, but can also be an array of strings. If an array
  240.              * is supplied (and the params 'list' and 'searchParams' are null), a
  241.              * combined feed of all users is displayed.
  242.              *
  243.              * Sample: 'tbillenstein' or ['twitterapi', '...', '...']
  244.              */
  245.             username: 'tbillenstein',
  246.  
  247.             /**
  248.              * The name of a user's list where the tweet feed is generated from. The special
  249.              * list name 'favorites' can be used to display a user's favorited tweets.
  250.              */
  251.             list: null,
  252.  
  253.             /**
  254.              * A single search param string or an array of search params, to be used in
  255.              * a Twitter search call. All Twitter Search Params are supported
  256.              * See here for the details:
  257.              * http://apiwiki.twitter.com/Twitter-Search-API-Method%3A-search
  258.              *
  259.              * Sample: 'q=twitter' or ['q=twitter', 'geocode=48.856667,2.350833,30km']
  260.              */
  261.             searchParams: null,
  262.  
  263.             /**
  264.              * The number of tweets shown in the tweet feed. If this param is 0, no feed
  265.              * is displayed. For user or list feeds the maximum count is 20, for search
  266.              * results the maximum count is 100.
  267.              *
  268.              * Unlike in previous releases, since 1.2.0 jTweetsAnywhere is based on a
  269.              * tweets caching algorithm that will always deliver the requested count of
  270.              * tweets accepting that this request can only be fullfilled by calling Twitter
  271.              * more than once.
  272.              *
  273.              * IMPORTANT: Please always keep in mind, that the use of the Twitter API is
  274.              * rate limited. Non-authenticated users are rated IP-based and you have only
  275.              * 150 calls per hour available. Every retrieval of tweets counts and so does
  276.              * for example hovering over a profile image to show the hovercard.
  277.              * jTweetsAnywhere will always check the remaining count of free API calls before
  278.              * actually calling Twitter to avoid black listing your visitor's IP.
  279.              */
  280.             count: 0,
  281.  
  282.             /**
  283.              * A flag (true/false) that specifies whether to display a profile image in
  284.              * tweets. If the param is set to null (the default value), a profile image
  285.              * is displayed only if the feed represents a user's list or the result of a
  286.              * Twitter search.
  287.              *
  288.              * THIS OPTION IS DEPRECATED. You should use showTweetFeed.showProfileImages
  289.              * instead.
  290.              */
  291.             tweetProfileImagePresent: null,
  292.  
  293.             /**
  294.              * Each tweet that is loaded from Twitter will pass the tweetFilter. if
  295.              * the filter returns true, the tweet will be added to the tweets cache
  296.              * otherwise it is ignored. The defaultTweetFilter alsways retruns true
  297.              * but you can supply your own tweet filter to customize the tweet feed.
  298.              */
  299.             tweetFilter: defaultTweetFilter,
  300.  
  301.             /**
  302.              * A flag (true/false) that specifies whether to display a Tweet Feed
  303.              * or an object literal representing the configuration options for the
  304.              * Tweet Feed. This flag works in conjunction with the count parameter:
  305.              * - if count equals 0, no feed is displayed, ignoring showTweetFeed
  306.              * - if count not equals 0 and showTweetFeed equals false, no feed
  307.              *   is displayed
  308.              * {
  309.              *     autoConformToTwitterStyleguide: false,
  310.              *                                     // Boolean - as the name implies, sets all options to confirm to Twitter's
  311.              *                                     // styleguide regulations. Implies:
  312.              *                                     // showTweetFeed: {
  313.              *                                     //     showUserFullNames: null,    // null means: if usernames are shown, show
  314.              *                                     //                                // fullnames too
  315.              *                                     //     showTwitterBird: true,
  316.              *                                    //     showActionReply: true,
  317.              *                                    //     showActionRetweet: true,
  318.              *                                    //     showActionFavorite: true
  319.              *                                     // }
  320.              *
  321.              *     showTwitterBird: true,        // Boolean - show Twitter bird icon beneath the timestamp of a tweet, linking to
  322.              *                                     // the tweeter's MiniProfile Web Intent
  323.              *
  324.              *     showTimestamp: true,         // A flag (true/false) that specifies whether to display a tweet's timestamp
  325.              *                                     // or an object literal representing the configuration options for the
  326.              *                                     // timestamp.
  327.              *                                     // {
  328.              *                                     //     refreshInterval: 0,    // Time in seconds to be waited until the
  329.              *                                     //                            // timestamps of the displayed tweets get refreshed
  330.              *                                    //                             // 0 means no refreshing.
  331.              *                                     // }
  332.              *
  333.              *     showSource: false,            // Boolean - Show info about the source of the tweet.
  334.              *
  335.              *     showGeoLocation: true,        // Boolean - Show geolocation info and link to Google maps.
  336.              *
  337.              *     showInReplyTo: true,            // Boolean - Show link to the "replied to" tweet (if available).
  338.              *
  339.              *     showActionReply: false,        // Boolean - Show tweet's 'Reply' action (supplies a link to popup the tweet's
  340.              *                                     // Reply Web Intent)
  341.              *
  342.              *     showActionRetweet: false,    // Boolean - Show tweet's 'Retweet' action (supplies a link to popup the tweet's
  343.              *                                     // Retweet Web Intent)
  344.              *
  345.              *     showActionFavorite: false,    // Boolean - Show tweet's 'Favorite' action (supplies a link to popup the tweet's
  346.              *                                     // Favorite Web Intent)
  347.              *
  348.              *     showProfileImages: null,        // A flag (true/false) that specifies whether to display a profile image in
  349.              *                                     // tweets. If the param is set to null (the default value), a profile image
  350.              *                                     // is displayed only if the feed represents a user's list or the result of a
  351.              *                                     // Twitter search.
  352.              *
  353.              *     showUserScreenNames: null,    // A flag (true/false/null) that specifies whether to display a username in
  354.              *                                     // tweets. If the param is set to null (the default value), a username
  355.              *                                     // is displayed only if the feed represents a user's list or the result of a
  356.              *                                     // Twitter search.
  357.              *
  358.              *     showUserFullNames: false,    // A flag (true/false/null) that specifies whether to display a user's full name
  359.              *                                     // in tweets. If the param is set to null, a user's full name
  360.              *                                     // is displayed only if the feed represents a user's list or the result of a
  361.              *                                     // Twitter search.
  362.              *
  363.              *     expandHovercards: false,        // Boolean - Show Hovercards in expanded mode.
  364.              *
  365.              *       includeRetweets: true,        // Boolean - Include native retweets in a user's tweet feed
  366.              *
  367.              *     paging:                        // An object literal representing the configuration options for the
  368.              *     {                            // paging support, that specifies how more/earlier tweets can be loaded
  369.              *         mode: "none"                   // by using the supplied UI controls (more/next buttons, scrollbar).
  370.              *     },                           // Accepted values for mode are: "none" | "more" | "prev-next" | "endless-scroll"
  371.              *                                    // if mode equals "endless-scroll" you have to set the height of the tweet feed
  372.              *                                    // element (.jta-tweet-list) in your CSS to get a scrollbar! You should also set
  373.              *                                    // the "overflow" attribute to "auto".
  374.              *
  375.              *     autorefresh:                    // An object literal representing the configuration options for the
  376.              *       {                            // autorefresh behaviour.
  377.              *
  378.              *                                    // IMPORTANT: Please always keep in mind, that using the Twitter API is rate
  379.              *                                    // limited. Non-authenticated users are rated IP-based and you have only 150
  380.              *                                    // calls per hour available. Every retrieval of tweets counts and so does for
  381.              *                                    // example hovering over a profile image to show the hovercard. jTweetsAnywhere will
  382.              *                                    // always check the remaining count of free API calls before actually calling
  383.              *                                    // Twitter to avoid black listing your visitor's IP.
  384.              *
  385.              *                                     // However - choose your settings wisely to keep your visitors happy. An update
  386.              *                                    // interval of 30 seconds on a feed that is updated averaged once per hour
  387.              *                                    // does not make sense and is a total waste of remaining API calls!
  388.              *
  389.              *           mode: "none",            // Accepted values for mode are: "none" | "auto-insert" | "trigger-insert"
  390.              *                                    // "none" (the default value) - disables the autorefresh feature
  391.              *                                    // "auto-insert" - automatically insert the new tweets on top of the tweet feed
  392.              *                                    // "trigger-insert" - if new tweets arrived, show or update a button that displays
  393.              *                                    // the number of new tweets. These new tweets are inserted on top of the tweet
  394.              *                                    // feed, if the user clicks on the button.
  395.              *
  396.              *           interval: 60,            // Time in seconds to be waited until the next request for new tweets. Minimum
  397.              *                                    // value is 30.
  398.              *
  399.              *         duration: 3600            // Time in seconds for how long the autorefresh will be active. After
  400.              *                                    // this period of time, autorefreshing will stop. A value of -1 means
  401.              *                                    // autorefresh for ever.
  402.              *    }
  403.              * }
  404.              */
  405.             showTweetFeed: true,
  406.  
  407.             /**
  408.              * A flag (true/false) that specifies whether to display a Twitter "Follow
  409.              * Button".
  410.              */
  411.             showFollowButton: false,
  412.  
  413.             /**
  414.              * A flag (true/false) that specifies whether to display a Twitter "Connect
  415.              * Button" or an object literal representing the configuration options for
  416.              * the "Tweet Box".
  417.              * {
  418.              *     size: 'medium'                // String - The size of the Connect Button. Valid values are: small, medium, large, xlarge
  419.              * }
  420.              */
  421.             showConnectButton: false,
  422.  
  423.             /**
  424.              * A flag (true/false) that specifies whether to display Login Infos.
  425.              */
  426.             showLoginInfo: false,
  427.  
  428.             /**
  429.              * A flag (true/false) that specifies whether to display a Twitter "Tweet
  430.              * Box" or an object literal representing the configuration options for
  431.              * the "Tweet Box".
  432.              * {
  433.              *     counter: true,                // Boolean - Display a counter in the Tweet Box for counting characters
  434.              *     width: 515,                    // Number - The width of the Tweet Box in pixels
  435.              *     height: 65,                    // Number - The height of the Tweet Box in pixels
  436.              *     label: "What's happening?",    // String - The text above the Tweet Box, a call to action
  437.              *     defaultContent: <none>,        // String - Pre-populated text in the Tweet Box. Useful for an @mention, a #hashtag, a link, etc.
  438.              *     onTweet: <none>                // Function - Specify a listener for when a tweet is sent from the Tweet Box. The listener receives two arguments: a plaintext tweet and an HTML tweet
  439.              * }
  440.              */
  441.             showTweetBox: false,
  442.  
  443.             /**
  444.              * Identifies the locale for I18N support. The default locale is 'en'. To use this option you have to inlude the
  445.              * adequate locale script, jtweetsanywhere-{language}-{version}.js, e.g. jtweetsanywhere-de-1.3.0.js
  446.              */
  447.             locale: 'en',
  448.  
  449.             /**
  450.              * A dataProvider is a function that delivers the "raw" Twitter data in
  451.              * JSON format. ATM internal use only!
  452.              */
  453.             tweetDataProvider:
  454.                 defaultTweetDataProvider,
  455.                 //mockedTweetDataProvider,
  456.             rateLimitDataProvider:
  457.                 defaultRateLimitDataProvider,
  458.                 //mockedRateLimitDataProvider,
  459.  
  460.             /**
  461.              * A decorator is a function that is responsible for constructing a certain
  462.              * element of the widget. Most of the decorators return a HTML string.
  463.              * Exceptions are the mainDecorator, which defines the basic sequence of
  464.              * the widget's components, plus the linkDecorator, the usernameDecorator
  465.              * and the hashtagDecorator which return the string that is supplied as a
  466.              * function param, enriched with the HTML tags.
  467.              *
  468.              * For details, see the implementations of the default decorators. Each
  469.              * default decorator can be overwritten by your own implementation.
  470.              */
  471.             mainDecorator: defaultMainDecorator,
  472.  
  473.             tweetFeedDecorator: defaultTweetFeedDecorator,
  474.  
  475.             tweetDecorator: defaultTweetDecorator,
  476.             tweetProfileImageDecorator: defaultTweetProfileImageDecorator,
  477.             tweetBodyDecorator: defaultTweetBodyDecorator,
  478.             tweetUsernameDecorator: defaultTweetUsernameDecorator,
  479.             tweetTextDecorator: defaultTweetTextDecorator,
  480.  
  481.             tweetAttributesDecorator: defaultTweetAttributesDecorator,
  482.             tweetTwitterBirdDecorator: defaultTweetTwitterBirdDecorator,
  483.             tweetTimestampDecorator: defaultTweetTimestampDecorator,
  484.             tweetSourceDecorator: defaultTweetSourceDecorator,
  485.             tweetGeoLocationDecorator: defaultTweetGeoLocationDecorator,
  486.             tweetInReplyToDecorator: defaultTweetInReplyToDecorator,
  487.             tweetRetweeterDecorator: defaultTweetRetweeterDecorator,
  488.  
  489.             tweetActionsDecorator: defaultTweetActionsDecorator,
  490.             tweetActionReplyDecorator: defaultTweetActionReplyDecorator,
  491.             tweetActionRetweetDecorator: defaultTweetActionRetweetDecorator,
  492.             tweetActionFavoriteDecorator: defaultTweetActionFavoriteDecorator,
  493.  
  494.             tweetFeedControlsDecorator: defaultTweetFeedControlsDecorator,
  495.             tweetFeedControlsMoreBtnDecorator: defaultTweetFeedControlsMoreBtnDecorator,
  496.             tweetFeedControlsPrevBtnDecorator: defaultTweetFeedControlsPrevBtnDecorator,
  497.             tweetFeedControlsNextBtnDecorator: defaultTweetFeedControlsNextBtnDecorator,
  498.  
  499.             tweetFeedAutorefreshTriggerDecorator: defaultTweetFeedAutorefreshTriggerDecorator,
  500.             tweetFeedAutorefreshTriggerContentDecorator: defaultTweetFeedAutorefreshTriggerContentDecorator,
  501.  
  502.             connectButtonDecorator: defaultConnectButtonDecorator,
  503.  
  504.             loginInfoDecorator: defaultLoginInfoDecorator,
  505.             loginInfoContentDecorator: defaultLoginInfoContentDecorator,
  506.  
  507.             followButtonDecorator: defaultFollowButtonDecorator,
  508.  
  509.             tweetBoxDecorator: defaultTweetBoxDecorator,
  510.  
  511.             linkDecorator: defaultLinkDecorator,
  512.             usernameDecorator: defaultUsernameDecorator,
  513.             hashtagDecorator: defaultHashtagDecorator,
  514.  
  515.             loadingDecorator: defaultLoadingDecorator,
  516.             errorDecorator: defaultErrorDecorator,
  517.             noDataDecorator: defaultNoDataDecorator,
  518.  
  519.             /**
  520.              * Formatters are currently used for date format processing only.
  521.              *
  522.              * The tweetTimestampFormatter formats the tweet's timestamp to be shown
  523.              * in the tweet attributes section
  524.              *
  525.              * For details, see the implementation of the defaultTweetTimestampFormatter.
  526.              */
  527.             tweetTimestampFormatter : defaultTweetTimestampFormatter,
  528.  
  529.             /**
  530.              * The tweetTimestampTooltipFormatter formats the tweet's timestamp to be shown
  531.              * in the tooltip when hovering over the timestamp link.
  532.              */
  533.             tweetTimestampTooltipFormatter : defaultTweetTimestampTooltipFormatter,
  534.  
  535.             /**
  536.              * A visualizer is a function that is responsible for adding one or more
  537.              * elements to the DOM and thereby making them visible to the user.
  538.              * A visualizer might also be responsible to do the opposite effect:
  539.              * To remove one or more elements from the DOM.
  540.              *
  541.              * The tweetVisualizer gets called each time a tweet element should be
  542.              * appended or prepended to the tweet feed element.
  543.              *
  544.              * For details, see the implementation of the defaultTweetVisualizer.
  545.              *
  546.              * Each default visualizer can be overwritten by your own implementation.
  547.              */
  548.             tweetVisualizer: defaultTweetVisualizer,
  549.  
  550.             /**
  551.              * The loadingIndicatorVisualizer gets called each time data is retrieved
  552.              * from Twitter to visualize the loading indicator. This visualizer is also
  553.              * used to hide the loading indicator.
  554.              *
  555.              * For details, see the implementation of the defaultLoadingIndicatorVisualizer.
  556.              */
  557.             loadingIndicatorVisualizer: defaultLoadingIndicatorVisualizer,
  558.  
  559.             /**
  560.              * The autorefreshTriggerVisualizer will be called if the autorefresh
  561.              * trigger should be visualized or hidden.
  562.              *
  563.              * For details, see the implementation of the autorefreshTriggerVisualizer.
  564.              */
  565.             autorefreshTriggerVisualizer: defaultAutorefreshTriggerVisualizer,
  566.  
  567.             /**
  568.              * An event handler is a function that gets called whenever the event you
  569.              * are interested in, occurs.
  570.              *
  571.              * The onDataRequest event handler will be called immediatly before calling
  572.              * Twitter to retrieve new data and gives you the opportunity to deny
  573.              * the call by returning false from the function.
  574.              *
  575.              * This feature might be used in conjunction with the paging feature,
  576.              * especially when using the "endless-scroll" paging mode, to avoid the
  577.              * exhaustion of remaining Twitter API calls, before the rate limit is
  578.              * reached. The stats parameter contains statistical infos and counters
  579.              * that you can examine to base your decision whether to return true or
  580.              * false.
  581.              */
  582.             onDataRequestHandler: defaultOnDataRequestHandler,
  583.  
  584.             /**
  585.              * The onRateLimitData event handler is called each time
  586.              * jTweetsAnywhere retrieved the rate limit data from Twitter. The actual
  587.              * rate limit data is contained in the stats object.
  588.              */
  589.             onRateLimitDataHandler: defaultOnRateLimitDataHandler,
  590.  
  591.             /**
  592.              * The OnOptionsInitializingHandler event handler is called before initializing
  593.              * the user options
  594.              */
  595.             onOptionsInitializingHandler: defaultOnOptionsInitializingHandler,
  596.  
  597.             _tweetFeedConfig:
  598.             {
  599.                 autoConformToTwitterStyleguide: false,
  600.                 showTwitterBird: true,
  601.                 showTimestamp:
  602.                 {
  603.                     refreshInterval: 0
  604.                 },
  605.                 showSource: false,
  606.                 showGeoLocation: true,
  607.                 showInReplyTo: true,
  608.                 showActionReply: false,
  609.                 showActionRetweet: false,
  610.                 showActionFavorite: false,
  611.                 showProfileImages: null,
  612.                 showUserScreenNames: null,
  613.                 showUserFullNames: false,
  614.                 expandHovercards: false,
  615.                 includeRetweets: true,
  616.                 paging:
  617.                 {
  618.                     mode: "none",
  619.                     _limit: 0,
  620.                     _offset: 0
  621.                 },
  622.                 autorefresh:
  623.                 {
  624.                     mode: "none",
  625.                     interval: 60,
  626.                     duration: 3600,
  627.                     max: -1,
  628.                     _startTime: null,
  629.                     _triggerElement: null
  630.                 },
  631.                 _pageParam: 0,
  632.                 _maxId: null,
  633.                 _recLevel: 0,
  634.                 _noData: false,
  635.                 _clearBeforePopulate: false
  636.             },
  637.             _tweetBoxConfig:
  638.             {
  639.                 counter: true,
  640.                 width: 515,
  641.                 height: 65,
  642.                 label: null,
  643.                 defaultContent: '',
  644.                 onTweet: function(textTweet, htmlTweet) {}
  645.             },
  646.             _connectButtonConfig:
  647.             {
  648.                 size: "medium"
  649.             },
  650.             _baseSelector: null,
  651.             _baseElement: null,
  652.             _tweetFeedElement: null,
  653.             _tweetFeedControlsElement: null,
  654.             _followButtonElement: null,
  655.             _loginInfoElement: null,
  656.             _connectButtonElement: null,
  657.             _tweetBoxElement: null,
  658.             _loadingIndicatorElement: null,
  659.             _noDataElement: null,
  660.             _tweetsCache: [],
  661.             _autorefreshTweetsCache: [],
  662.             _stats:
  663.             {
  664.                 dataRequestCount: 0,
  665.                 rateLimitPreventionCount: 0,
  666.                 rateLimit:
  667.                 {
  668.                     remaining_hits: 150,
  669.                     hourly_limit: 150
  670.                 }
  671.             },
  672.             _resourceBundle: null
  673.         }, config);
  674.  
  675.         // save the plugin's base selector
  676.         options._baseSelector = this.selector;
  677.  
  678.         options.onOptionsInitializingHandler(options);
  679.         setupOptions(options);
  680.  
  681.         // no main decorator? nothing to do!
  682.         if (!options.mainDecorator)
  683.         {
  684.             return;
  685.         }
  686.  
  687.         $.ajaxSetup({ cache: true });
  688.  
  689.         return this.each(function()
  690.         {
  691.             // the DOM element, where to display the widget
  692.             options._baseElement = $(this);
  693.  
  694.             // create the widget's necessary sub DOM elements
  695.             options._tweetFeedElement = options.tweetFeedDecorator ? $(options.tweetFeedDecorator(options)) : null;
  696.             options._tweetFeedControlsElement = options.tweetFeedControlsDecorator ? $(options.tweetFeedControlsDecorator(options)) : null;
  697.             options._followButtonElement = options.followButtonDecorator ? $(options.followButtonDecorator(options)) : null;
  698.             options._tweetBoxElement = options.tweetBoxDecorator ? $(options.tweetBoxDecorator(options)) : null;
  699.             options._connectButtonElement = options.connectButtonDecorator ? $(options.connectButtonDecorator(options)): null;
  700.             options._loginInfoElement = options.loginInfoDecorator ? $(options.loginInfoDecorator(options)) : null;
  701.  
  702.             // add the widget to the DOM
  703.             options.mainDecorator(options);
  704.  
  705.             populateTweetFeed(options);
  706.             populateAnywhereControls(options);
  707.  
  708.             bindEventHandlers(options);
  709.  
  710.             setupAutorefresh(options);
  711.         });
  712.     };
  713.     defaultMainDecorator = function(options)
  714.     {
  715.         // defines the default sequence of the widget's elements
  716.         if (options._tweetFeedElement)
  717.         {
  718.             options._baseElement.append(options._tweetFeedElement);
  719.         }
  720.  
  721.         if (options._tweetFeedControlsElement)
  722.         {
  723.             options._baseElement.append(options._tweetFeedControlsElement);
  724.         }
  725.  
  726.         if (options._connectButtonElement)
  727.         {
  728.             options._baseElement.append(options._connectButtonElement);
  729.         }
  730.  
  731.         if (options._loginInfoElement)
  732.         {
  733.             options._baseElement.append(options._loginInfoElement);
  734.         }
  735.  
  736.         if (options._followButtonElement)
  737.         {
  738.             options._baseElement.append(options._followButtonElement);
  739.         }
  740.  
  741.         if (options._tweetBoxElement)
  742.         {
  743.             options._baseElement.append(options._tweetBoxElement);
  744.         }
  745.     };
  746.     defaultTweetFeedControlsDecorator = function(options)
  747.     {
  748.         // the default tweet feed's paging controls
  749.         var html = '';
  750.  
  751.         if (options._tweetFeedConfig.paging.mode == 'prev-next')
  752.         {
  753.             if (options.tweetFeedControlsPrevBtnDecorator)
  754.             {
  755.                 html += options.tweetFeedControlsPrevBtnDecorator(options);
  756.             }
  757.  
  758.             if (options.tweetFeedControlsNextBtnDecorator)
  759.             {
  760.                 html += options.tweetFeedControlsNextBtnDecorator(options);
  761.             }
  762.         }
  763.         else if (options._tweetFeedConfig.paging.mode == 'endless-scroll')
  764.         {
  765.             // nothing to do here
  766.         }
  767.         else
  768.         {
  769.             if (options.tweetFeedControlsMoreBtnDecorator)
  770.             {
  771.                 html += options.tweetFeedControlsMoreBtnDecorator(options);
  772.             }
  773.         }
  774.  
  775.         return '<div class="jta-tweet-list-controls">' + html + '</div>';
  776.     };
  777.     defaultTweetFeedControlsMoreBtnDecorator = function(options)
  778.     {
  779.         return '<span class="jta-tweet-list-controls-button jta-tweet-list-controls-button-more">' + options._resourceBundle._('More') + '</span>';
  780.     };
  781.     defaultTweetFeedControlsPrevBtnDecorator = function(options)
  782.     {
  783.         return '<span class="jta-tweet-list-controls-button jta-tweet-list-controls-button-prev">' + options._resourceBundle._('Prev') + '</span>';
  784.     };
  785.     defaultTweetFeedControlsNextBtnDecorator = function(options)
  786.     {
  787.         return '<span class="jta-tweet-list-controls-button jta-tweet-list-controls-button-next">' + options._resourceBundle._('Next') + '</span>';
  788.     };
  789.     defaultTweetFeedAutorefreshTriggerDecorator = function(count, options)
  790.     {
  791.         var html = '';
  792.  
  793.         if (options.tweetFeedAutorefreshTriggerContentDecorator)
  794.         {
  795.             html = options.tweetFeedAutorefreshTriggerContentDecorator(count, options);
  796.         }
  797.  
  798.         return '<li class="jta-tweet-list-autorefresh-trigger">' + html + '</li>';
  799.     };
  800.     defaultTweetFeedAutorefreshTriggerContentDecorator = function(count, options)
  801.     {
  802.         var content = options._resourceBundle.__('%count% new tweet', '%count% new tweets', count, { '%count%' : count });
  803.  
  804.         return '<span class="jta-tweet-list-autorefresh-trigger-content">' + content + '</span>';
  805.     };
  806.     defaultTweetFeedDecorator = function(options)
  807.     {
  808.         // the default placeholder for the tweet feed is an unordered list
  809.         return '<ul class="jta-tweet-list"></ul>';
  810.     };
  811.     defaultTweetDecorator = function(tweet, options)
  812.     {
  813.         // the default tweet is made of the optional user's profile image and the
  814.         // tweet body inside a list item element
  815.         var html = '';
  816.  
  817.         if (options._tweetFeedConfig.showProfileImages)
  818.         {
  819.             html += options.tweetProfileImageDecorator(tweet, options);
  820.         }
  821.  
  822.         if (options.tweetBodyDecorator)
  823.         {
  824.             html += options.tweetBodyDecorator(tweet, options);
  825.         }
  826.  
  827.         return '<li class="jta-tweet-list-item">' + html + '</li>';
  828.     };
  829.     defaultTweetProfileImageDecorator = function(tweet, options)
  830.     {
  831.         // if tweet is a native retweet, use the retweet's profile
  832.         var t = tweet.retweeted_status || tweet;
  833.  
  834.         // the default profile image decorator simply adds a link to the user's Twitter profile
  835.         var screenName = getScreenName(tweet);
  836.         var imageUrl = t.user ? t.user.profile_image_url : false || t.profile_image_url;
  837.  
  838.         var html =
  839.             '<a class="jta-tweet-profile-image-link" href="http://twitter.com/' + screenName + '" target="_blank">' +
  840.             '<img src="' + imageUrl + '" alt="' + screenName + '"' +
  841.             (isAnywherePresent() ? '' : (' title="' + screenName + '"')) +
  842.             '/>' +
  843.             '</a>';
  844.  
  845.         return '<div class="jta-tweet-profile-image">' + html + '</div>';
  846.     };
  847.     defaultTweetBodyDecorator = function(tweet, options)
  848.     {
  849.         // the default tweet body contains the tweet text and the tweet's creation date
  850.         var html = '';
  851.  
  852.         if (options.tweetTextDecorator)
  853.         {
  854.             html += options.tweetTextDecorator(tweet, options);
  855.         }
  856.  
  857.         if (options.tweetAttributesDecorator)
  858.         {
  859.             html += options.tweetAttributesDecorator(tweet, options);
  860.         }
  861.  
  862.         if (options.tweetActionsDecorator)
  863.         {
  864.             html += options.tweetActionsDecorator(tweet, options);
  865.         }
  866.  
  867.         return '<div class="jta-tweet-body ' +
  868.             (options._tweetFeedConfig.showProfileImages ? 'jta-tweet-body-list-profile-image-present' : '') + '">' +
  869.             html +
  870.             '</div>';
  871.     };
  872.     defaultTweetTextDecorator = function(tweet, options)
  873.     {
  874.         var tweetText = tweet.text;
  875.  
  876.         // if usernames should be visible and tweet is a native retweet, use
  877.         // the original tweet text
  878.         if (tweet.retweeted_status &&
  879.             (
  880.                 options._tweetFeedConfig.showUserScreenNames ||
  881.                 options._tweetFeedConfig.showUserScreenNames == null ||
  882.                 options._tweetFeedConfig.showUserFullNames ||
  883.                 options._tweetFeedConfig.showUserFullNames == null
  884.             )
  885.         )
  886.         {
  887.             tweetText = tweet.retweeted_status.text;
  888.         }
  889.  
  890.         // the default tweet text decorator optionally marks links, @usernames,
  891.         // and #hashtags
  892.         if (options.linkDecorator)
  893.         {
  894.             tweetText = options.linkDecorator(tweetText, options);
  895.         }
  896.  
  897.         if (options.usernameDecorator)
  898.         {
  899.             tweetText = options.usernameDecorator(tweetText, options);
  900.         }
  901.  
  902.         if (options.hashtagDecorator)
  903.         {
  904.             tweetText = options.hashtagDecorator(tweetText, options);
  905.         }
  906.  
  907.         if (options._tweetFeedConfig.showUserScreenNames ||
  908.             options._tweetFeedConfig.showUserFullNames ||
  909.             tweet.retweeted_status &&
  910.             (
  911.                 options._tweetFeedConfig.showUserScreenNames == null ||
  912.                 options._tweetFeedConfig.showUserFullNames == null
  913.             )
  914.         )
  915.         {
  916.             tweetText = options.tweetUsernameDecorator(tweet, options) + ' ' + tweetText;
  917.         }
  918.  
  919.         return '<p class="jta-tweet-text">' + tweetText + '</p>';
  920.     };
  921.     defaultTweetUsernameDecorator = function(tweet, options)
  922.     {
  923.         // if tweet is a native retweet, use the retweet's profile
  924.         var screenName = getScreenName(tweet);
  925.         var fullName = getFullName(tweet);
  926.  
  927.         var htmlScreenName = null;
  928.         if (screenName && (options._tweetFeedConfig.showUserScreenNames || (options._tweetFeedConfig.showUserScreenNames == null && tweet.retweeted_status)))
  929.         {
  930.             htmlScreenName =
  931.                 '<span class="jta-tweet-user-screen-name">' +
  932.                 '<a class="jta-tweet-user-screen-name-link" href="http://twitter.com/' + screenName + '" target="_blank">' +
  933.                 screenName +
  934.                 '</a>' +
  935.                 '</span>';
  936.         }
  937.  
  938.         var htmlFullName = null;
  939.         if (fullName && (options._tweetFeedConfig.showUserFullNames || (options._tweetFeedConfig.showUserFullNames == null && tweet.retweeted_status)))
  940.         {
  941.             htmlFullName =
  942.                 '<span class="jta-tweet-user-full-name">' +
  943.                 (htmlScreenName ? ' ' : '') +
  944.                 '<a class="jta-tweet-user-full-name-link" href="http://twitter.com/' + screenName + '" name="' + screenName + '" target="_blank">' +
  945.                 fullName +
  946.                 '</a>' +
  947.                 '</span>';
  948.         }
  949.  
  950.         var html = '';
  951.  
  952.         if (htmlScreenName)
  953.         {
  954.             html += htmlScreenName;
  955.         }
  956.  
  957.         if (htmlFullName)
  958.         {
  959.             if (htmlScreenName)
  960.             {
  961.                 html += ' ';
  962.             }
  963.  
  964.             html += htmlFullName;
  965.         }
  966.  
  967.         if (htmlScreenName || htmlFullName)
  968.         {
  969.             html =
  970.                 '<span class="jta-tweet-user-name">' +
  971.                 (tweet.retweeted_status ? 'RT ' : '') +
  972.                 html +
  973.                 '</span>';
  974.         }
  975.  
  976.         return html;
  977.     };
  978.     defaultTweetAttributesDecorator = function(tweet, options)
  979.     {
  980.         var html = '';
  981.  
  982.         if (options.tweetTwitterBirdDecorator ||
  983.             options.tweetTimestampDecorator ||
  984.             options.tweetSourceDecorator ||
  985.             options.tweetGeoLocationDecorator ||
  986.             options.tweetInReplyToDecorator ||
  987.             (tweet.retweeted_status && options.tweetRetweeterDecorator)
  988.         )
  989.         {
  990.  
  991.             if (options.tweetTwitterBirdDecorator)
  992.             {
  993.                 html += options.tweetTwitterBirdDecorator(tweet, options);
  994.             }
  995.  
  996.             if (options.tweetTimestampDecorator)
  997.             {
  998.                 html += options.tweetTimestampDecorator(tweet, options);
  999.             }
  1000.  
  1001.             if (options.tweetSourceDecorator)
  1002.             {
  1003.                 html += options.tweetSourceDecorator(tweet, options);
  1004.             }
  1005.  
  1006.             if (options.tweetGeoLocationDecorator)
  1007.             {
  1008.                 html += options.tweetGeoLocationDecorator(tweet, options);
  1009.             }
  1010.  
  1011.             if (options.tweetInReplyToDecorator)
  1012.             {
  1013.                 html += options.tweetInReplyToDecorator(tweet, options);
  1014.             }
  1015.  
  1016.             if (tweet.retweeted_status && options.tweetRetweeterDecorator)
  1017.             {
  1018.                 html += options.tweetRetweeterDecorator(tweet, options);
  1019.             }
  1020.         }
  1021.  
  1022.         return html;
  1023.     };
  1024.     defaultTweetTimestampDecorator = function(tweet, options)
  1025.     {
  1026.         // the default tweet timestamp decorator does a little bit of Twitter like formatting.
  1027.  
  1028.         // if tweet is a native retweet, use the retweet's timestamp
  1029.         var tw = tweet.retweeted_status || tweet;
  1030.  
  1031.         // reformat timestamp from Twitter, so IE is happy
  1032.         var createdAt = formatDate(tw.created_at);
  1033.  
  1034.         // format the timestamp by the tweetTimestampFormatter
  1035.         var tweetTimestamp = options.tweetTimestampFormatter(createdAt, options);
  1036.         var tweetTimestampTooltip = options.tweetTimestampTooltipFormatter(createdAt);
  1037.  
  1038.         var html =
  1039.             '<abbr class="jta-tweet-timestamp-link published" data-timestamp="' + createdAt +
  1040.             '" title="' +
  1041.             tweetTimestampTooltip + '">' +
  1042.             tweetTimestamp +
  1043.             '</abbr>';
  1044.  
  1045.         return html;
  1046.     };
  1047.     defaultTweetTwitterBirdDecorator = function(tweet, options)
  1048.     {
  1049.         var screenName = getScreenName(tweet);
  1050.         var intentUrl = 'https://twitter.com/intent/user?screen_name=' + screenName;
  1051.         var linkTitle = screenName + ' ' + options._resourceBundle._('on Twitter');
  1052.  
  1053.         var html =
  1054.             '<span class="jta-tweet-twitter-bird">' +
  1055.             '<a href="' + intentUrl + '" target="_blank" title="' + linkTitle + '">' +
  1056.             '<span class="jta-tweet-twitter-bird-icon"> </span>' +
  1057.             '</a>' +
  1058.             '</span>';
  1059.  
  1060.         return html;
  1061.     };
  1062.     defaultTweetTimestampTooltipFormatter = function(timeStamp)
  1063.     {
  1064.         var d = new Date(timeStamp);
  1065.  
  1066.         return d.toLocaleString();
  1067.     };
  1068.     defaultTweetTimestampFormatter = function(timeStamp, options)
  1069.     {
  1070.         var now = new Date();
  1071.  
  1072.         var diff = parseInt((now.getTime() - Date.parse(timeStamp)) / 1000);
  1073.  
  1074.         var tweetTimestamp = '';
  1075.         if (diff < 60)
  1076.         {
  1077.             tweetTimestamp += options._resourceBundle.__('%secs% second ago', '%secs% seconds ago', diff, { '%secs%': diff });
  1078.         }
  1079.         else if (diff < 3600)
  1080.         {
  1081.             var t = parseInt((diff + 30) / 60);
  1082.             tweetTimestamp += options._resourceBundle.__('%mins% minute ago', '%mins% minutes ago', t, { '%mins%': t });
  1083.         }
  1084.         else if (diff < 86400)
  1085.         {
  1086.             var t = parseInt((diff + 1800) / 3600);
  1087.             tweetTimestamp += options._resourceBundle.__('%hours% hour ago', '%hours% hours ago', t, { '%hours%': t });
  1088.         }
  1089.         else
  1090.         {
  1091.             var t = parseInt((diff + 43200) / 86400);
  1092.             tweetTimestamp += options._resourceBundle.__('%days% day ago', '%days% days ago', t, { '%days%': t });
  1093.         }
  1094.  
  1095.         return tweetTimestamp;
  1096.     };
  1097.     defaultTweetSourceDecorator = function(tweet, options)
  1098.     {
  1099.         // if tweet is a native retweet, use the retweet's source
  1100.         var tw = tweet.retweeted_status || tweet;
  1101.  
  1102.         var source = tw.source.replace(/\<\;/gi,'<').replace(/\>\;/gi,'>').replace(/\"\;/gi,'"');
  1103.         var html =
  1104.             '<span class="jta-tweet-source">' +
  1105.             ' ' + options._resourceBundle._('via') + ' ' +
  1106.             '<span class="jta-tweet-source-link">' +
  1107.             source +
  1108.             '</span>' +
  1109.             '</span>';
  1110.  
  1111.         return html;
  1112.     };
  1113.     defaultTweetGeoLocationDecorator = function(tweet, options)
  1114.     {
  1115.         var html = '';
  1116.  
  1117.         // if tweet is a native retweet, use the retweet's source
  1118.         var tw = tweet.retweeted_status || tweet;
  1119.  
  1120.         var q = null;
  1121.         if (tw.geo && tw.geo.coordinates)
  1122.         {
  1123.             q = tw.geo.coordinates.join();
  1124.         }
  1125.         else if (tw.place && tw.place.full_name)
  1126.         {
  1127.             q = tw.place.full_name;
  1128.         }
  1129.  
  1130.         if (q)
  1131.         {
  1132.             var location = options._resourceBundle._('here');
  1133.             if (tw.place && tw.place.full_name)
  1134.             {
  1135.                 location = tw.place.full_name;
  1136.             }
  1137.  
  1138.             var link = 'http://maps.google.com/maps?q=' + q;
  1139.  
  1140.             html =
  1141.                 '<span class="jta-tweet-location">' +
  1142.                 ' ' + options._resourceBundle._('from') + ' ' +
  1143.                 '<a class="jta-tweet-location-link" href="' + link + '" target="_blank">' +
  1144.                 location +
  1145.                 '</a>' +
  1146.                 '</span>';
  1147.         }
  1148.  
  1149.         return html;
  1150.     };
  1151.     defaultTweetInReplyToDecorator = function(tweet, options)
  1152.     {
  1153.         // if tweet is a native retweet, use the retweet's source
  1154.         var tw = tweet.retweeted_status || tweet;
  1155.  
  1156.         var html = '';
  1157.  
  1158.         if (tw.in_reply_to_status_id && tw.in_reply_to_screen_name)
  1159.         {
  1160.             var linkHref = 'http://twitter.com/' + tw.in_reply_to_screen_name + '/status/' + tw.in_reply_to_status_id;
  1161.             var linkText = options._resourceBundle._('in reply to') + ' ' + tw.in_reply_to_screen_name;
  1162.  
  1163.             html =
  1164.                 '<span class="jta-tweet-inreplyto">' +
  1165.                 ' ' +
  1166.                 '<a class="jta-tweet-inreplyto-link" href="' + linkHref + '" target="_blank">' +
  1167.                 linkText +
  1168.                 '</a>' +
  1169.                 '</span>';
  1170.         }
  1171.  
  1172.         return html;
  1173.     };
  1174.     defaultTweetRetweeterDecorator = function(tweet, options)
  1175.     {
  1176.         var html = '';
  1177.  
  1178.         if (tweet.retweeted_status)
  1179.         {
  1180.             var screenName = getUserScreenName(tweet);
  1181.  
  1182.             var rtc = (tweet.retweeted_status.retweet_count || 0) - 1;
  1183.  
  1184.             var link =
  1185.                 '<a class="jta-tweet-retweeter-link" href="http://twitter.com/' + screenName + '" target="_blank">' +
  1186.                 screenName +
  1187.                 '</a>';
  1188.  
  1189.             var rtcount = options._resourceBundle.__(' and %rtc% other', ' and %rtc% others', rtc, { '%rtc%': rtc });
  1190.  
  1191.             html =
  1192.                 '<br/>' +
  1193.                 '<span class="jta-tweet-retweeter">' +
  1194.                 options._resourceBundle._('Retweeted by') + ' ' + link +
  1195.                 (rtc > 0 ? rtcount : '') +
  1196.                 '</span>';
  1197.         }
  1198.  
  1199.         return html;
  1200.     };
  1201.     defaultTweetActionsDecorator = function(tweet, options)
  1202.     {
  1203.         var html = '';
  1204.  
  1205.         if (options.tweetActionReplyDecorator ||
  1206.             options.tweetActionRetweetDecorator ||
  1207.             options.tweetActionFavoriteDecorator
  1208.         )
  1209.         {
  1210.             html += '<span class="jta-tweet-actions">';
  1211.  
  1212.             if (options.tweetActionReplyDecorator)
  1213.             {
  1214.                 html += options.tweetActionReplyDecorator(tweet, options);
  1215.             }
  1216.  
  1217.             if (options.tweetActionRetweetDecorator)
  1218.             {
  1219.                 html += options.tweetActionRetweetDecorator(tweet, options);
  1220.             }
  1221.  
  1222.             if (options.tweetActionFavoriteDecorator)
  1223.             {
  1224.                 html += options.tweetActionFavoriteDecorator(tweet, options);
  1225.             }
  1226.  
  1227.             html += '</span>';
  1228.         }
  1229.  
  1230.         return html;
  1231.     };
  1232.     defaultTweetActionReplyDecorator = function(tweet, options)
  1233.     {
  1234.         var intentUrl = 'https://twitter.com/intent/tweet?in_reply_to=' + tweet.id;
  1235.         var actionLabel = options._resourceBundle._('Reply');
  1236.  
  1237.         var html =
  1238.             '<span class="jta-tweet-action-reply">' +
  1239.             '<a href="' + intentUrl + '">' + actionLabel + '</a>' +
  1240.             '</span>';
  1241.  
  1242.         return html;
  1243.     };
  1244.     defaultTweetActionRetweetDecorator = function(tweet, options)
  1245.     {
  1246.         var intentUrl = 'https://twitter.com/intent/retweet?tweet_id=' + tweet.id;
  1247.         var actionLabel = options._resourceBundle._('Retweet');
  1248.  
  1249.         var html =
  1250.             '<span class="jta-tweet-action-retweet">' +
  1251.             '<a href="' + intentUrl + '">' + actionLabel + '</a>' +
  1252.             '</span>';
  1253.  
  1254.         return html;
  1255.     };
  1256.     defaultTweetActionFavoriteDecorator = function(tweet, options)
  1257.     {
  1258.         var intentUrl = 'https://twitter.com/intent/favorite?tweet_id=' + tweet.id;
  1259.         var actionLabel = options._resourceBundle._('Favorite');
  1260.  
  1261.         var html =
  1262.             '<span class="jta-tweet-action-favorite">' +
  1263.             '<a href="' + intentUrl + '">' + actionLabel + '</a>' +
  1264.             '</span>';
  1265.  
  1266.         return html;
  1267.     };
  1268.     defaultConnectButtonDecorator = function(options)
  1269.     {
  1270.         // the default placeholder for the @Anywhere ConnectButton
  1271.         return '<div class="jta-connect-button"></div>';
  1272.     };
  1273.     defaultLoginInfoDecorator = function(options)
  1274.     {
  1275.         // the default placeholder for the LoginInfo
  1276.         return '<div class="jta-login-info"></div>';
  1277.     };
  1278.     defaultLoginInfoContentDecorator = function(options, T)
  1279.     {
  1280.         // the default markup of the LoginInfo content: the user's profile image, the
  1281.         // user's screen_name and a "button" to sign out
  1282.         var html = '';
  1283.  
  1284.         if (T.isConnected())
  1285.         {
  1286.             var screenName = T.currentUser.data('screen_name');
  1287.             var imageUrl = T.currentUser.data('profile_image_url');
  1288.  
  1289.             html =
  1290.                 '<div class="jta-login-info-profile-image">' +
  1291.                 '<a href="http://twitter.com/' + screenName + '" target="_blank">' +
  1292.                 '<img src="' + imageUrl + '" alt="' + screenName + '" title="' + screenName + '"/>' +
  1293.                 '</a>' +
  1294.                 '</div>' +
  1295.                 '<div class="jta-login-info-block">' +
  1296.                 '<div class="jta-login-info-screen-name">' +
  1297.                 '<a href="http://twitter.com/' + screenName + '" target="_blank">' + screenName + '</a>' +
  1298.                 '</div>' +
  1299.                 '<div class="jta-login-info-sign-out">' +
  1300.                 options._resourceBundle._('Sign out') +
  1301.                 '</div>' +
  1302.                 '</div>' +
  1303.                 '<div class="jta-clear"> </div>'
  1304.                 ;
  1305.         }
  1306.  
  1307.         return html;
  1308.     };
  1309.     defaultFollowButtonDecorator = function(options)
  1310.     {
  1311.         // the default placeholder for the @Anywhere FollowButton
  1312.         return '<div class="jta-follow-button"></div>';
  1313.     };
  1314.     defaultTweetBoxDecorator = function(options)
  1315.     {
  1316.         // the default placeholder for the @Anywhere TweetBox
  1317.         return '<div class="jta-tweet-box"></div>';
  1318.     };
  1319.     defaultLinkDecorator = function(text, options)
  1320.     {
  1321.         // the regex to markup links
  1322.         return text.replace(/((ftp|http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?)/gi,'<a href="$1" class="jta-tweet-a jta-tweet-link" target="_blank" rel="nofollow">$1<\/a>');
  1323.     };
  1324.     defaultUsernameDecorator = function(text, options)
  1325.     {
  1326.         // the regex to markup @usernames. if @Anywhere is present the task is left to
  1327.         // them
  1328.         return isAnywherePresent() ? text : text.replace(/\B@(\w+)/gi,'@<a href="http://twitter.com/$1" class="jta-tweet-a twitter-anywhere-user" target="_blank" rel="nofollow">$1<\/a>');
  1329.     };
  1330.     defaultHashtagDecorator = function(text, options)
  1331.     {
  1332.         // the regex to markup #hashtags
  1333.         return text.replace(/#([a-zA-Z0-9_]+)/gi,'<a href="http://search.twitter.com/search?q=%23$1" class="jta-tweet-a jta-tweet-hashtag" title="#$1" target="_blank" rel="nofollow">#$1<\/a>');
  1334.     };
  1335.     defaultLoadingDecorator = function(options)
  1336.     {
  1337.         // the default loading decorator simply says: loading ...
  1338.         return '<li class="jta-loading"> <span class="loader"></span> </li>';
  1339.     };
  1340.     defaultErrorDecorator = function(errorText, options)
  1341.     {
  1342.         // the default error decorator shows the error message
  1343.         return '<li class="jta-error">' + options._resourceBundle._('ERROR') + ': ' + errorText + '</li>';
  1344.     };
  1345.     defaultNoDataDecorator = function(options)
  1346.     {
  1347.         // the default no-data decorator simply says: No more data
  1348.         return '<li class="jta-nodata">' + options._resourceBundle._('No more data') + '</li>';
  1349.     };
  1350.  
  1351.     defaultTweetFilter = function(tweet, options)
  1352.     {
  1353.         return true;
  1354.     };
  1355.  
  1356.     defaultTweetVisualizer = function(tweetFeedElement, tweetElement, inserter, options)
  1357.     {
  1358.         // insert (append/prepend) the tweetElement to the tweetFeedElement
  1359.         tweetFeedElement[inserter](tweetElement);
  1360.     };
  1361.     defaultLoadingIndicatorVisualizer = function(tweetFeedElement, loadingIndicatorElement, options, callback)
  1362.     {
  1363.         defaultVisualizer(tweetFeedElement, loadingIndicatorElement, 'append', 'fadeIn', 600, 'fadeOut', 200, callback);
  1364.     };
  1365.     defaultAutorefreshTriggerVisualizer = function(tweetFeedElement, triggerElement, options, callback)
  1366.     {
  1367.         defaultVisualizer(tweetFeedElement, triggerElement, 'prepend', 'slideDown', 600, 'fadeOut', 200, callback);
  1368.     };
  1369.     defaultVisualizer = function(container, element, inserter, effectIn, durationIn, effectOut, durationOut, callback)
  1370.     {
  1371.         // if param container is null element has to be removed from
  1372.         // the DOM, else element has to be inserted in container
  1373.  
  1374.         // if param callback is not null, the callback function must be called
  1375.         // in any case, if the visualizer is done
  1376.  
  1377.         var cb = function()
  1378.         {
  1379.             if (callback)
  1380.             {
  1381.                 callback();
  1382.             }
  1383.         };
  1384.  
  1385.         if (container)
  1386.         {
  1387.             element.hide();
  1388.             container[inserter](element);
  1389.             element[effectIn](durationIn, cb);
  1390.         }
  1391.         else
  1392.         {
  1393.             element[effectOut](durationOut, function()
  1394.             {
  1395.                 element.remove();
  1396.                 cb();
  1397.             });
  1398.         }
  1399.     };
  1400.     defaultOnDataRequestHandler = function(stats, options)
  1401.     {
  1402.         return true;
  1403.     };
  1404.     defaultOnRateLimitDataHandler = function(stats, options)
  1405.     {
  1406.     };
  1407.     defaultOnOptionsInitializingHandler = function(options)
  1408.     {
  1409.     };
  1410.     updateLoginInfoElement = function(options, T)
  1411.     {
  1412.         // update the content of the LoginInfo element
  1413.         if (options._loginInfoElement && options.loginInfoContentDecorator)
  1414.         {
  1415.             options._loginInfoElement.children().remove();
  1416.             options._loginInfoElement.append(options.loginInfoContentDecorator(options, T));
  1417.             $(options._baseSelector + ' .jta-login-info-sign-out').bind('click', function()
  1418.             {
  1419.                 twttr.anywhere.signOut();
  1420.             });
  1421.         }
  1422.     };
  1423.     getFeedUrl = function(options, flPaging)
  1424.     {
  1425.         // create the Twitter API URL based on the configuration options
  1426.         var url = ('https:' == document.location.protocol ? 'https:' : 'http:');
  1427.  
  1428.         if (options.searchParams)
  1429.         {
  1430.             url += '//search.twitter.com/search.json?' +
  1431.                 ((options.searchParams instanceof Array) ? options.searchParams.join('&') : options.searchParams) +
  1432.                 '&rpp=100';
  1433.         }
  1434.         else if (options.list)
  1435.         {
  1436.             if ('favorites' == options.list)
  1437.             {
  1438.                 url += '//api.twitter.com/1/favorites/' + options.username + '.json?count=20';
  1439.             }
  1440.             else
  1441.             {
  1442.                 url += '//api.twitter.com/1/' + options.username + '/lists/' + options.list + '/statuses.json?per_page=20';
  1443.             }
  1444.         }
  1445.         else
  1446.         {
  1447.             url += '//api.twitter.com/1/statuses/user_timeline.json?screen_name=' + options.username + '&count=20';
  1448.             if (options._tweetFeedConfig.includeRetweets)
  1449.                 url += '&include_rts=true';
  1450.         }
  1451.  
  1452.         if (flPaging)
  1453.         {
  1454.             url +=
  1455.                 (options._tweetFeedConfig._maxId ? '&max_id=' + options._tweetFeedConfig._maxId : '') +
  1456.                 '&page=' + options._tweetFeedConfig._pageParam;
  1457.         }
  1458.  
  1459.         url += '&callback=?';
  1460.  
  1461.         return url;
  1462.     };
  1463.     isAnywherePresent = function()
  1464.     {
  1465.         // check, if @Anywhere is present
  1466.         return (typeof(twttr) != 'undefined' && typeof(twttr.anywhere) != 'undefined');
  1467.     };
  1468.     clearTweetFeed = function(options)
  1469.     {
  1470.         if (options._tweetFeedElement)
  1471.         {
  1472.             options._tweetFeedElement.empty();
  1473.         }
  1474.     };
  1475.     setupOptions = function(options)
  1476.     {
  1477.         options._resourceBundle = JTA_I18N.getResourceBundle('jTweetsAnywhere', options.locale);
  1478.  
  1479.         options._tweetBoxConfig.label = options._resourceBundle._("What's happening?");
  1480.  
  1481.         // if username is an array, create the search query and flatten username
  1482.         if (typeof(options.username) != 'string')
  1483.         {
  1484.             if (!options.searchParams)
  1485.             {
  1486.                 options.searchParams = ['q=from:' + options.username.join(" OR from:")];
  1487.             }
  1488.  
  1489.             options.username = options.username[0];
  1490.         }
  1491.  
  1492.         // if showTweetFeed is not set to a boolean value, we expect the configuration of
  1493.         // the tweet feed
  1494.         if (typeof(options.showTweetFeed) == 'object')
  1495.         {
  1496.             $.extend(true, options._tweetFeedConfig, options.showTweetFeed);
  1497.         }
  1498.  
  1499.         // if showTweetBox is not set to a boolean value, we expect the configuration of
  1500.         // the TweetBox
  1501.         if (typeof(options.showTweetBox) == 'object')
  1502.         {
  1503.             $.extend(true, options._tweetBoxConfig, options.showTweetBox);
  1504.             options.showTweetBox = true;
  1505.         }
  1506.  
  1507.         // if showConnectButton is not set to a boolean value, we expect the
  1508.         // configuration of the Connect Button
  1509.         if (typeof(options.showConnectButton) == 'object')
  1510.         {
  1511.             options._connectButtonConfig = options.showConnectButton;
  1512.             options.showConnectButton = true;
  1513.         }
  1514.  
  1515.         // to be compatible, check the deprecated option 'tweetProfileImagePresent'
  1516.         if (options._tweetFeedConfig.showProfileImages == null)
  1517.         {
  1518.             options._tweetFeedConfig.showProfileImages = options.tweetProfileImagePresent;
  1519.         }
  1520.  
  1521.         // if _tweetFeedConfig.showProfileImages is not set to a boolean value,
  1522.         // we decide to show a profile image if the feed represents a user's
  1523.         // list or the results of a Twitter search
  1524.         if (options._tweetFeedConfig.showProfileImages == null)
  1525.         {
  1526.             options._tweetFeedConfig.showProfileImages = (options.list || options.searchParams) && options.tweetProfileImageDecorator;
  1527.         }
  1528.  
  1529.         // handle the autoConformToTwitterStyleguide
  1530.         if (options._tweetFeedConfig.autoConformToTwitterStyleguide)
  1531.         {
  1532.             options._tweetFeedConfig.showUserFullNames = null;
  1533.             options._tweetFeedConfig.showTwitterBird = true;
  1534.             options._tweetFeedConfig.showActionReply = true;
  1535.             options._tweetFeedConfig.showActionRetweet = true;
  1536.             options._tweetFeedConfig.showActionFavorite = true;
  1537.         }
  1538.  
  1539.         // if _tweetFeedConfig.showUserScreenNames is not set to a boolean value,
  1540.         // we decide to show a username if the feed represents a user's
  1541.         // list or the results of a Twitter search or a tweet is a native retweet
  1542.         if (options._tweetFeedConfig.showUserScreenNames == null)
  1543.         {
  1544.             if (options.list || options.searchParams)
  1545.             {
  1546.                 options._tweetFeedConfig.showUserScreenNames = true;
  1547.             }
  1548.  
  1549.             if (!options.tweetUsernameDecorator)
  1550.             {
  1551.                 options._tweetFeedConfig.showUserScreenNames = false;
  1552.             }
  1553.         }
  1554.  
  1555.         // if _tweetFeedConfig.showUserFullNames is not set to a boolean value,
  1556.         // we decide to show a user's full name if the feed represents a user's
  1557.         // list or the results of a Twitter search or a tweet is a native retweet
  1558.         if (options._tweetFeedConfig.showUserFullNames == null)
  1559.         {
  1560.             if (options.list || options.searchParams)
  1561.             {
  1562.                 options._tweetFeedConfig.showUserFullNames = true;
  1563.             }
  1564.  
  1565.             if (!options.tweetUsernameDecorator)
  1566.             {
  1567.                 options._tweetFeedConfig.showUserFullNames = false;
  1568.             }
  1569.         }
  1570.  
  1571.  
  1572.         options.count = validateRange(options.count, 0, options.searchParams ? 100 : 20);
  1573.  
  1574.         options._tweetFeedConfig.autorefresh.interval = Math.max(30, options._tweetFeedConfig.autorefresh.interval);
  1575.         if (options._tweetFeedConfig.autorefresh.max <= 0)
  1576.         {
  1577.             options._tweetFeedConfig.autorefresh.max = -1;
  1578.         }
  1579.         options._tweetFeedConfig.paging._offset = 0;
  1580.         options._tweetFeedConfig.paging._limit = options.count;
  1581.  
  1582.         // internally, the decision of what parts of a widget are to be
  1583.         // displayed is based on the existence of the decorators
  1584.         if (options.count == 0 || !options.showTweetFeed)
  1585.         {
  1586.             options.tweetFeedDecorator = null;
  1587.             options.tweetFeedControlsDecorator = null;
  1588.         }
  1589.  
  1590.         if (options._tweetFeedConfig.paging.mode == 'none')
  1591.         {
  1592.             options.tweetFeedControlsDecorator = null;
  1593.         }
  1594.  
  1595.         if (!options.showFollowButton)
  1596.         {
  1597.             options.followButtonDecorator = null;
  1598.         }
  1599.  
  1600.         if (!options.showTweetBox)
  1601.         {
  1602.             options.tweetBoxDecorator = null;
  1603.         }
  1604.  
  1605.         if (!options.showConnectButton)
  1606.         {
  1607.             options.connectButtonDecorator = null;
  1608.         }
  1609.  
  1610.         if (!options.showLoginInfo)
  1611.         {
  1612.             options.loginInfoDecorator = null;
  1613.         }
  1614.  
  1615.         if (!options._tweetFeedConfig.showTwitterBird)
  1616.         {
  1617.             options.tweetTwitterBirdDecorator = null;
  1618.         }
  1619.  
  1620.         if (!options._tweetFeedConfig.showTimestamp)
  1621.         {
  1622.             options.tweetTimestampDecorator = null;
  1623.         }
  1624.  
  1625.         if (!options._tweetFeedConfig.showSource)
  1626.         {
  1627.             options.tweetSourceDecorator = null;
  1628.         }
  1629.  
  1630.         if (!options._tweetFeedConfig.showGeoLocation)
  1631.         {
  1632.             options.tweetGeoLocationDecorator = null;
  1633.         }
  1634.  
  1635.         if (!options._tweetFeedConfig.showInReplyTo)
  1636.         {
  1637.             options.tweetInReplyToDecorator = null;
  1638.         }
  1639.  
  1640.         if (!options._tweetFeedConfig.showActionReply)
  1641.         {
  1642.             options.tweetActionReplyDecorator = null;
  1643.         }
  1644.  
  1645.         if (!options._tweetFeedConfig.showActionRetweet)
  1646.         {
  1647.             options.tweetActionRetweetDecorator = null;
  1648.         }
  1649.  
  1650.         if (!options._tweetFeedConfig.showActionFavorite)
  1651.         {
  1652.             options.tweetActionFavoriteDecorator = null;
  1653.         }
  1654.     };
  1655.     setupAutorefresh = function(options)
  1656.     {
  1657.         options._tweetFeedConfig.autorefresh._startTime = new Date().getTime();
  1658.  
  1659.         startAutorefresh(options);
  1660.         startTimestampRefresh(options);
  1661.     };
  1662.     populateTweetFeed = function(options)
  1663.     {
  1664.         // if a tweet feed is to be displayed, get the tweets and show them
  1665.         if (options.tweetDecorator && options._tweetFeedElement)
  1666.         {
  1667.             getPagedTweets(options, function(tweets, options)
  1668.             {
  1669.                 if (options._tweetFeedConfig._clearBeforePopulate)
  1670.                 {
  1671.                     clearTweetFeed(options);
  1672.                 }
  1673.  
  1674.                 hideLoadingIndicator(options, function()
  1675.                 {
  1676.                     // process the tweets
  1677.                     $.each(tweets, function(idx, tweet)
  1678.                     {
  1679.                         // decorate the tweet and give it to the tweet visualizer
  1680.                         options.tweetVisualizer(
  1681.                             options._tweetFeedElement,
  1682.                             $(options.tweetDecorator(tweet, options)),
  1683.                             'append',
  1684.                             options
  1685.                         );
  1686.                     });
  1687.  
  1688.                     if (options._tweetFeedConfig._noData && options.noDataDecorator && !options._tweetFeedConfig._noDataElement)
  1689.                     {
  1690.                         options._tweetFeedConfig._noDataElement = $(options.noDataDecorator(options));
  1691.                         options._tweetFeedElement.append(options._tweetFeedConfig._noDataElement);
  1692.                     }
  1693.  
  1694.                     if (options._tweetFeedConfig._clearBeforePopulate)
  1695.                     {
  1696.                         options._tweetFeedElement.scrollTop(0);
  1697.                     }
  1698.  
  1699.                     addHovercards(options);
  1700.                 });
  1701.             });
  1702.         }
  1703.     };
  1704.     populateTweetFeed2 = function(options)
  1705.     {
  1706.         if (options._tweetFeedElement && options._autorefreshTweetsCache.length > 0)
  1707.         {
  1708.             if (options._tweetFeedConfig.autorefresh.mode == 'trigger-insert')
  1709.             {
  1710.                 if (options._tweetFeedConfig.autorefresh._triggerElement)
  1711.                 {
  1712.                     if (options.tweetFeedAutorefreshTriggerContentDecorator)
  1713.                     {
  1714.                         options._tweetFeedConfig.autorefresh._triggerElement.html(
  1715.                             options.tweetFeedAutorefreshTriggerContentDecorator(options._autorefreshTweetsCache.length, options)
  1716.                         );
  1717.                     }
  1718.                 }
  1719.                 else
  1720.                 {
  1721.                     if (options.tweetFeedAutorefreshTriggerDecorator)
  1722.                     {
  1723.                         options._tweetFeedConfig.autorefresh._triggerElement =
  1724.                             $(options.tweetFeedAutorefreshTriggerDecorator(options._autorefreshTweetsCache.length, options));
  1725.                         options._tweetFeedConfig.autorefresh._triggerElement.bind('click', function()
  1726.                         {
  1727.                             options.autorefreshTriggerVisualizer(
  1728.                                 null,
  1729.                                 options._tweetFeedConfig.autorefresh._triggerElement,
  1730.                                 options,
  1731.                                 function()
  1732.                                 {
  1733.                                     insertTriggerTweets(options);
  1734.                                 }
  1735.                             );
  1736.                             options._tweetFeedConfig.autorefresh._triggerElement = null;
  1737.                         });
  1738.  
  1739.                         options.autorefreshTriggerVisualizer(options._tweetFeedElement, options._tweetFeedConfig.autorefresh._triggerElement, options);
  1740.                     }
  1741.                 }
  1742.             }
  1743.             else
  1744.             {
  1745.                 insertTriggerTweets(options);
  1746.             }
  1747.         }
  1748.     };
  1749.     insertTriggerTweets = function(options)
  1750.     {
  1751.         // populate the tweet feed with tweets from the autorefresh cache
  1752.         if (options.tweetDecorator && options._autorefreshTweetsCache.length > 0)
  1753.         {
  1754.             // process the autorefresh cache
  1755.             while (options._autorefreshTweetsCache.length > 0)
  1756.             {
  1757.                 // get the last tweet and remove it from the autorefresh cache
  1758.                 var tweet = options._autorefreshTweetsCache.pop();
  1759.  
  1760.                 // put that tweet on top of the tweets cache
  1761.                 options._tweetsCache.unshift(tweet);
  1762.  
  1763.                 // adjust paging offset
  1764.                 options._tweetFeedConfig.paging._offset++;
  1765.  
  1766.                 // decorate the tweet and give it to the tweet visualizer
  1767.                 options.tweetVisualizer(
  1768.                     options._tweetFeedElement,
  1769.                     $(options.tweetDecorator(tweet, options)),
  1770.                     'prepend',
  1771.                     options
  1772.                 );
  1773.             }
  1774.  
  1775.             addHovercards(options);
  1776.         }
  1777.     };
  1778.     addHovercards = function(options)
  1779.     {
  1780.         if (isAnywherePresent())
  1781.         {
  1782.             // if @Anywhere is present, append Hovercards to @username and
  1783.             // profile images
  1784.             twttr.anywhere(function(T)
  1785.             {
  1786.                 T(options._baseSelector + ' .jta-tweet-list').hovercards({expanded: options._tweetFeedConfig.expandHovercards});
  1787.                 T(options._baseSelector + ' .jta-tweet-profile-image img').hovercards(
  1788.                 {
  1789.                     expanded: options._tweetFeedConfig.expandHovercards,
  1790.                     username: function(node) { return node.alt; }
  1791.                 });
  1792.                 T(options._baseSelector + ' .jta-tweet-retweeter-link').hovercards(
  1793.                 {
  1794.                     expanded: options._tweetFeedConfig.expandHovercards,
  1795.                     username: function(node) { return node.text; }
  1796.                 });
  1797.                 T(options._baseSelector + ' .jta-tweet-user-screen-name-link').hovercards(
  1798.                 {
  1799.                     expanded: options._tweetFeedConfig.expandHovercards,
  1800.                     username: function(node) { return node.text; }
  1801.                 });
  1802.                 T(options._baseSelector + ' .jta-tweet-user-full-name-link').hovercards(
  1803.                 {
  1804.                     expanded: options._tweetFeedConfig.expandHovercards,
  1805.                     username: function(node) { return node.name; }
  1806.                 });
  1807.             });
  1808.         }
  1809.     };
  1810.     populateAnywhereControls = function(options)
  1811.     {
  1812.         if (isAnywherePresent())
  1813.         {
  1814.             twttr.anywhere(function(T)
  1815.             {
  1816.                 // optionally add an @Anywhere TweetBox
  1817.                 if (options.tweetBoxDecorator)
  1818.                 {
  1819.                     T(options._baseSelector + ' .jta-tweet-box').tweetBox(options._tweetBoxConfig);
  1820.                 }
  1821.  
  1822.                 // optionally add an @Anywhere FollowButton
  1823.                 if (options.followButtonDecorator)
  1824.                 {
  1825.                     T(options._baseSelector + ' .jta-follow-button').followButton(options.username);
  1826.                 }
  1827.  
  1828.                 // optionally add an @Anywhere ConnectButton
  1829.                 if (options.connectButtonDecorator)
  1830.                 {
  1831.                     var o = $.extend(
  1832.                     {
  1833.                         authComplete: function(user)
  1834.                         {
  1835.                             // display/update login infos on connect/signin event
  1836.                             updateLoginInfoElement(options, T);
  1837.                         },
  1838.                         signOut: function()
  1839.                         {
  1840.                             // display/update login infos on signout event
  1841.                             updateLoginInfoElement(options, T);
  1842.                         }
  1843.                     }, options._connectButtonConfig);
  1844.  
  1845.                     T(options._baseSelector + ' .jta-connect-button').connectButton(o);
  1846.  
  1847.                     // display/update login infos
  1848.                     updateLoginInfoElement(options, T);
  1849.                 }
  1850.             });
  1851.         }
  1852.     };
  1853.     bindEventHandlers = function(options)
  1854.     {
  1855.         if (options.tweetFeedControlsDecorator)
  1856.         {
  1857.             if (options._tweetFeedConfig.paging.mode == 'prev-next')
  1858.             {
  1859.                 $(options._baseSelector + ' .jta-tweet-list-controls-button-prev').bind('click', function()
  1860.                 {
  1861.                     if (!isLoading(options) && options._tweetFeedConfig.paging._offset > 0)
  1862.                     {
  1863.                         prevPage(options, true);
  1864.                     }
  1865.                 });
  1866.                 $(options._baseSelector + ' .jta-tweet-list-controls-button-next').bind('click', function()
  1867.                 {
  1868.                     if (!isLoading(options))
  1869.                     {
  1870.                         nextPage(options, true);
  1871.                     }
  1872.                 });
  1873.             }
  1874.             else if (options._tweetFeedConfig.paging.mode == 'endless-scroll')
  1875.             {
  1876.                 options._tweetFeedElement.bind("scroll", function()
  1877.                 {
  1878.                     if (!isLoading(options) && ($(this)[0].scrollHeight - $(this).scrollTop() == $(this).outerHeight()))
  1879.                     {
  1880.                         nextPage(options, false);
  1881.                     }
  1882.                 });
  1883.             }
  1884.             else
  1885.             {
  1886.                 $(options._baseSelector + ' .jta-tweet-list-controls-button-more').bind('click', function()
  1887.                 {
  1888.                     if (!isLoading(options))
  1889.                     {
  1890.                         nextPage(options, false);
  1891.                     }
  1892.                 });
  1893.             }
  1894.         }
  1895.     };
  1896.     nextPage = function(options, flClear)
  1897.     {
  1898.         doPage(options, flClear, Math.min(options._tweetFeedConfig.paging._offset + options._tweetFeedConfig.paging._limit, options._tweetsCache.length));
  1899.     };
  1900.     prevPage = function(options, flClear)
  1901.     {
  1902.         doPage(options, flClear, Math.max(0, options._tweetFeedConfig.paging._offset - options._tweetFeedConfig.paging._limit));
  1903.     };
  1904.     doPage = function(options, flClear, newOffset)
  1905.     {
  1906.         options._tweetFeedConfig.paging._offset = newOffset;
  1907.         options._tweetFeedConfig._clearBeforePopulate = flClear;
  1908.  
  1909.         populateTweetFeed(options);
  1910.     };
  1911.     startAutorefresh = function(options)
  1912.     {
  1913.         if (options._tweetFeedConfig.autorefresh.mode != 'none' &&
  1914.             options._tweetFeedConfig.paging.mode != 'prev-next' &&
  1915.             options._tweetFeedConfig.autorefresh.duration != 0 &&
  1916.             (
  1917.                 options._tweetFeedConfig.autorefresh.duration < 0 ||
  1918.                 (new Date().getTime() - options._tweetFeedConfig.autorefresh._startTime) <= options._tweetFeedConfig.autorefresh.duration * 1000
  1919.             )
  1920.         )
  1921.         {
  1922.             window.setTimeout(function() { processAutorefresh(options); }, options._tweetFeedConfig.autorefresh.interval * 1000);
  1923.         }
  1924.     };
  1925.     stopAutorefresh = function(options)
  1926.     {
  1927.         options._tweetFeedConfig.autorefresh.duration = 0;
  1928.     };
  1929.     processAutorefresh = function(options)
  1930.     {
  1931.         if (options._tweetFeedConfig.autorefresh.duration != 0)
  1932.         {
  1933.             // load the data ...
  1934.             getRateLimitedData(options, true, getFeedUrl(options, false), function(data, options)
  1935.             {
  1936.                 // reverse the sequence of the autorefresh tweets ...
  1937.                 var tweets = (data.results || data).slice(0);
  1938.                 tweets.reverse();
  1939.  
  1940.                 // ...then process them
  1941.                 $.each(tweets, function(idx, tweet)
  1942.                 {
  1943.                     // Snowflake support: just update ids that are currently used
  1944.                     if (tweet.id_str)
  1945.                     {
  1946.                         tweet.id = tweet.id_str;
  1947.                     }
  1948.  
  1949.                     if (tweet.in_reply_to_status_id_str)
  1950.                     {
  1951.                         tweet.in_reply_to_status_id = tweet.in_reply_to_status_id_str;
  1952.                     }
  1953.  
  1954.                     // if this tweet is already in one of the tweet caches, ignore it
  1955.                     if (!isTweetInAutorefreshCache(tweet, options) && !isTweetInCache(tweet, options))
  1956.                     {
  1957.                         // optionally filter tweet ...
  1958.                         if (options.tweetFilter(tweet, options))
  1959.                         {
  1960.                             // ... then put it to the top of the autorefresh cache
  1961.                             options._autorefreshTweetsCache.unshift(tweet);
  1962.  
  1963.                             // if a maximum autorefresh cache size is configured, remove elder tweets
  1964.                             if (options._tweetFeedConfig.autorefresh.max > 0)
  1965.                             {
  1966.                                 while (options._autorefreshTweetsCache.length > options._tweetFeedConfig.autorefresh.max)
  1967.                                 {
  1968.                                     options._autorefreshTweetsCache.pop();
  1969.                                 }
  1970.                             }
  1971.                         }
  1972.                     }
  1973.                 });
  1974.  
  1975.                 populateTweetFeed2(options);
  1976.             });
  1977.  
  1978.             // restart autorefresh
  1979.             startAutorefresh(options);
  1980.         }
  1981.     };
  1982.     startTimestampRefresh = function(options)
  1983.     {
  1984.         if (
  1985.             options.tweetTimestampDecorator &&
  1986.             typeof(options._tweetFeedConfig.showTimestamp) == 'object' &&
  1987.             options._tweetFeedConfig.showTimestamp.refreshInterval > 0
  1988.         )
  1989.         {
  1990.             window.setTimeout(function() { processTimestampRefresh(options); }, options._tweetFeedConfig.showTimestamp.refreshInterval * 1000);
  1991.         }
  1992.     };
  1993.     processTimestampRefresh = function(options)
  1994.     {
  1995.         $.each(options._tweetFeedElement.find('.jta-tweet-timestamp-link'), function(idx, element)
  1996.         {
  1997.             var dataTimestamp = $(element).attr('data-timestamp');
  1998.  
  1999.             $(element).html(options.tweetTimestampFormatter(dataTimestamp, options));
  2000.         });
  2001.  
  2002.         startTimestampRefresh(options);
  2003.     };
  2004.     isTweetInCache = function(tweet, options)
  2005.     {
  2006.         var l = options._tweetsCache.length;
  2007.  
  2008.         for (var i = 0; i < l; i++)
  2009.         {
  2010.             if (tweet.id == options._tweetsCache[i].id)
  2011.             {
  2012.                 return true;
  2013.             }
  2014.         }
  2015.  
  2016.         return false;
  2017.     };
  2018.     isTweetInAutorefreshCache = function(tweet, options)
  2019.     {
  2020.         var l = options._autorefreshTweetsCache.length;
  2021.  
  2022.         for (var i = 0; i < l; i++)
  2023.         {
  2024.             if (tweet.id == options._autorefreshTweetsCache[i].id)
  2025.             {
  2026.                 return true;
  2027.             }
  2028.         }
  2029.  
  2030.         return false;
  2031.     };
  2032.     showLoadingIndicator = function(options)
  2033.     {
  2034.         if (options._tweetFeedElement && options.loadingDecorator && !options._loadingIndicatorElement)
  2035.         {
  2036.             options._loadingIndicatorElement = $(options.loadingDecorator(options));
  2037.             options.loadingIndicatorVisualizer(options._tweetFeedElement, options._loadingIndicatorElement, options, null);
  2038.             options._tweetFeedElement.scrollTop(1000000);
  2039.         }
  2040.     };
  2041.     hideLoadingIndicator = function(options, callback)
  2042.     {
  2043.         if (options._loadingIndicatorElement)
  2044.         {
  2045.             options.loadingIndicatorVisualizer(null, options._loadingIndicatorElement, options, callback);
  2046.             options._loadingIndicatorElement = null;
  2047.         }
  2048.         else
  2049.         {
  2050.             if (callback)
  2051.             {
  2052.                 callback();
  2053.             }
  2054.         }
  2055.     };
  2056.     isLoading = function(options)
  2057.     {
  2058.         return options._loadingIndicatorElement != null;
  2059.     };
  2060.     formatDate = function(dateStr)
  2061.     {
  2062.         return dateStr.replace(/^([a-z]{3})( [a-z]{3} \d\d?)(.*)( \d{4})$/i, '$1,$2$4$3');
  2063.     };
  2064.     getUserScreenName = function(tweet)
  2065.     {
  2066.         var screenName = tweet.user ? tweet.user.screen_name : false || tweet.from_user;
  2067.  
  2068.         return screenName;
  2069.     };
  2070.     getScreenName = function(tweet)
  2071.     {
  2072.         var t = tweet.retweeted_status || tweet;
  2073.         var screenName = t.user ? t.user.screen_name : false || t.from_user;
  2074.  
  2075.         return screenName;
  2076.     };
  2077.     getFullName = function(tweet)
  2078.     {
  2079.         var t = tweet.retweeted_status || tweet;
  2080.         var fullName = t.user ? t.user.name : undefined;
  2081.  
  2082.         return fullName;
  2083.     };
  2084.     validateRange = function(num, lo, hi)
  2085.     {
  2086.         if (num < lo)
  2087.             num = lo;
  2088.  
  2089.         if (num > hi)
  2090.             num = hi;
  2091.  
  2092.         return num;
  2093.     };
  2094.     showError = function(options, errorText)
  2095.     {
  2096.         if (options.errorDecorator && options._tweetFeedElement)
  2097.         {
  2098.             options._tweetFeedElement.append(options.errorDecorator(errorText, options));
  2099.         }
  2100.     };
  2101.     getPagedTweets = function(options, callback)
  2102.     {
  2103.         options._tweetFeedConfig._recLevel = 0;
  2104.  
  2105.         getRecPagedTweets(options, options._tweetFeedConfig.paging._offset, options._tweetFeedConfig.paging._limit, callback);
  2106.     };
  2107.     getRecPagedTweets = function(options, offset, limit, callback)
  2108.     {
  2109.         ++options._tweetFeedConfig._recLevel;
  2110.  
  2111.         if (offset + limit <= options._tweetsCache.length ||
  2112.             options._tweetFeedConfig._recLevel > 3 ||
  2113.             options._tweetFeedConfig._noData
  2114.         )
  2115.         {
  2116.             // if the requested data is already cached or the max. no. of
  2117.             // consecutive API calls is reached, use the records
  2118.  
  2119.             if (offset + limit > options._tweetsCache.length)
  2120.             {
  2121.                 limit = Math.max(0, options._tweetsCache.length - offset);
  2122.             }
  2123.  
  2124.             var tweets = [];
  2125.  
  2126.             for (var i = 0; i < limit; i++)
  2127.             {
  2128.                 tweets[i] = options._tweetsCache[offset + i];
  2129.             }
  2130.  
  2131.             callback(tweets, options);
  2132.         }
  2133.         else
  2134.         {
  2135.             // ... if not, load the data, fill the cache and try again
  2136.             ++options._tweetFeedConfig._pageParam;
  2137.  
  2138.             getRateLimitedData(options, false, getFeedUrl(options, true), function(data, options)
  2139.             {
  2140.                 var tweets = data.results || data;
  2141.  
  2142.                 if (tweets.length == 0)
  2143.                 {
  2144.                     options._tweetFeedConfig._noData = true;
  2145.                    }
  2146.                 else
  2147.                 {
  2148.                     $.each(tweets, function(idx, tweet)
  2149.                     {
  2150.                         // Snowflake support: just update ids that are currently used
  2151.                         if (tweet.id_str)
  2152.                         {
  2153.                             tweet.id = tweet.id_str;
  2154.                         }
  2155.  
  2156.                         if (tweet.in_reply_to_status_id_str)
  2157.                         {
  2158.                             tweet.in_reply_to_status_id = tweet.in_reply_to_status_id_str;
  2159.                         }
  2160.  
  2161.                         // save the first tweet id for subsequent paging requests
  2162.                         if (!options._tweetFeedConfig._maxId)
  2163.                         {
  2164.                             options._tweetFeedConfig._maxId = tweet.id;
  2165.                         }
  2166.  
  2167.                         // optionally filter tweet ...
  2168.                         if (options.tweetFilter(tweet, options))
  2169.                         {
  2170.                             // then put it into the cache
  2171.                             options._tweetsCache.push(tweet);
  2172.                         }
  2173.                     });
  2174.                 }
  2175.  
  2176.                 getRecPagedTweets(options, offset, limit, callback);
  2177.             });
  2178.         }
  2179.     };
  2180.     getRateLimitedData = function(options, flAutorefresh, url, callback)
  2181.     {
  2182.         getRateLimit(options, function(rateLimit)
  2183.         {
  2184.             if (rateLimit && rateLimit.remaining_hits <= 0)
  2185.             {
  2186.                 options._stats.rateLimitPreventionCount++;
  2187.                 hideLoadingIndicator(options, null);
  2188.                 return;
  2189.             }
  2190.  
  2191.             getData(options, flAutorefresh, url, callback);
  2192.         });
  2193.     };
  2194.     getData = function(options, flAutorefresh, url, callback)
  2195.     {
  2196.         options._stats.dataRequestCount++;
  2197.  
  2198.         if (!options.onDataRequestHandler(options._stats, options))
  2199.         {
  2200.             hideLoadingIndicator(options, null);
  2201.             return;
  2202.         }
  2203.  
  2204.         if (!flAutorefresh)
  2205.         {
  2206.             showLoadingIndicator(options);
  2207.         }
  2208.  
  2209.         options.tweetDataProvider(url, function(data)
  2210.         {
  2211.             if (data.error)
  2212.             {
  2213.                 // in case of an error, display the error message
  2214.                 showError(options, data.error);
  2215.             }
  2216.             else
  2217.             {
  2218.                 callback(data, options);
  2219.             }
  2220.         });
  2221.     };
  2222.     getRateLimit = function(options, callback)
  2223.     {
  2224.         options.rateLimitDataProvider(function(rateLimit)
  2225.         {
  2226.             options._stats.rateLimit = rateLimit;
  2227.  
  2228.             options.onRateLimitDataHandler(options._stats, options);
  2229.  
  2230.             callback(rateLimit);
  2231.         });
  2232.     };
  2233.     defaultTweetDataProvider = function(url, callback)
  2234.     {
  2235.         $.getJSON(url, callback);
  2236.     };
  2237.     defaultRateLimitDataProvider = function(callback)
  2238.     {
  2239.         $.getJSON('http://api.twitter.com/1/account/rate_limit_status.json?callback=?', callback);
  2240.     };
  2241. })(jQuery);
  2242.